package io.jt808.proto;

//import io.jt808.util.ProtoUtils;

import io.norland.proto.AbstractWrapper;
import io.norland.buffer.AutoBuffer;
import io.norland.proto.IProto;
import io.jt808.util.Tools;
//import io.norland.proto.ProtoUtils;
import io.norland.proto.ProtoCreator;
import org.apache.log4j.Logger;

/**
 * 采用 0x7e 表示，若校验码、消息头以及消息体中出现 0x7e，则要进行转义处理，转义
 * 规则定义如下：
 * 0x7e <————> 0x7d 后紧跟一个 0x02；
 * 0x7d <————> 0x7d 后紧跟一个 0x01。
 * 转义处理过程如下：
 * 发送消息时：消息封装——>计算并填充校验码——>转义；
 * 接收消息时：转义还原——>验证校验码——>解析消息
 */
public class Wrapper extends AbstractWrapper implements IProto {
    public static final byte SEPARATOR = 0x7E;//0x7E;
    public static final int MIN_LENGTH = 1 + 12 + 1 + 1;//最小长度是不分包情况下

    private Logger logger = Logger.getLogger(Wrapper.class);
    private WrapperHeader header = new WrapperHeader();

    /**
     * 如果是分包，分包是不能解析的，等待和其他包合成一个完整的包才能解析
     */
    private byte[] childPacket;
    /**
     * 非分包时存储信息
     */
    private Object messageContents;

    private String errorMessage;

    private ProtoCreator protoCreator;

    public Wrapper() {
        protoCreator = ProtoCreator.getInstance(this);
    }

    @Override
    public byte[] writeToBytes() {
        AutoBuffer buffer = new AutoBuffer();

        buffer.writeBytes(header.writeToBytes());
        if (!header.isPackage()) {
            if (messageContents instanceof IProto)
                buffer.writeBytes(((IProto) messageContents).writeToBytes());
            else if (messageContents instanceof byte[])
                buffer.writeBytes((byte[]) messageContents);
        } else
            buffer.writeBytes(childPacket);
        byte[] messageBytes = buffer.array();
        byte checkSum = getCheckXor(messageBytes, 0, messageBytes.length);
        buffer.writeByte(checkSum);

        byte[] escapedBytes = enEscape(buffer.array());
        buffer.clear();
        buffer.writeByte(Wrapper.SEPARATOR);
        buffer.writeBytes(escapedBytes);
        buffer.writeByte(Wrapper.SEPARATOR);
        return buffer.array();

    }

    @Override
    public void readFromBuf(AutoBuffer buff) {
        byte[] messageBytes = deEscape(buff.array());
        AutoBuffer buffer = new AutoBuffer(messageBytes);
        try {
            // 检测校验码
            byte xor = getCheckXor(messageBytes, 1, messageBytes.length - 2);
            byte realXor = messageBytes[messageBytes.length - 2];
            if (xor == realXor) {
                buffer.markReaderIndex();
                buffer.skipBytes(1 + 2);
                int messageBodyProperty = buffer.readUnsignedShort();
                buffer.resetReaderIndex();
                boolean isPackage = (messageBodyProperty & (1 << 13)) != 0;
                int headerBytes = isPackage ? 16 : 12;
                buffer.skipBytes(1);//分隔符
                header.readFromBuf(buffer.slice(1, headerBytes));
                buffer.skipBytes(headerBytes);
                if (header.isPackage()) {
                    // 分包的消息体是纯数据不进行解析，保留在消息中.
                    childPacket = buffer.readBytes(buffer.readableBytes() - 1 - 1);
                } else {
                    // 其余的包都进行解析
                    int bodyLen = buffer.readableBytes() - 1 - 1;
                    String protoName = requestProtocol();
                    IProto proto = (IProto) protoCreator.create(protoName);
                    if (proto != null) {
                        proto.readFromBuf(buffer.slice(buffer.readerIndex(), bodyLen));
                        messageContents = proto;
                    }
                    buffer.skipBytes(bodyLen);
                }
                buffer.skipBytes(1 + 1);//检验位2与分隔符
            } else {
                errorMessage = "校验码不正确";
                logger.error(errorMessage + ": " + Tools.ToHexFormatString(messageBytes));
            }
        } catch (Exception ex) {
            logger.error("Wrapper : readFromBuf() " + Tools.ToHexFormatString(messageBytes));
            errorMessage = "解析异常:" + ex.getMessage();
            logger.error(errorMessage, ex);
        }
    }

    public void readPackedData(AutoBuffer buffer) {
        try {
            String protoName = requestProtocol();
            IProto proto = (IProto) protoCreator.create(protoName);
            if (proto != null) {
                proto.readFromBuf(buffer);
                messageContents = proto;
            }
        } catch (Exception ex) {
            logger.error("Wrapper : readPackedData() ");
            errorMessage = "解析异常:" + ex.getMessage();
            logger.error(errorMessage, ex);
        }
    }

    public String requestProtocol() {
        //四位大写十六进制
        return String.format("%04X", header.getMessageId());
    }

    @Override
    public String getTerminalSerialNo() {
        return header.getSimId();
    }

    public int currentPackageNo() {
        return header.getMessagePacketNo();
    }

    public int totalPackages() {
        return header.getMessageTotalPacketsCount();
    }

    public boolean isPacked() {
        return header.isPackage();
    }

    public byte[] getPackageData() {
        return childPacket;
    }

    public Object getMessageContents() {
        return messageContents;
    }

    public void setMessageContents(Object messageContents) {
        this.messageContents = messageContents;
    }

    public WrapperHeader getHeader() {
        return header;
    }

    public void setHeader(WrapperHeader header) {
        this.header = header;
    }

    public byte[] getChildPacket() {
        return childPacket;
    }

    public void setChildPacket(byte[] childPacket) {
        this.childPacket = childPacket;
    }

    /**
     * 获取校验和
     */
    private byte getCheckXor(byte[] data, int pos, int len) {
        byte b = 0;
        for (int i = pos; i < len; i++) {
            b ^= data[i];
        }
        return b;
    }

    /**
     * 将标识字符的转义字符还原
     */
    private byte[] deEscape(byte[] data) {
        AutoBuffer buff = new AutoBuffer();
        for (int i = 0; i < data.length; i++) {
            if (data[i] == 0x7D) {
                if (data[i + 1] == 0x01) {
                    buff.writeByte((byte) 0x7D);
                    i++;
                } else if (data[i + 1] == 0x02) {
                    buff.writeByte((byte) 0x7E);
                    i++;
                }
            } else {
                buff.writeByte(data[i]);
            }
        }

        return buff.array();
    }

    /**
     * 加入标示符的转义进行封装
     */
    private byte[] enEscape(byte[] data) {
        AutoBuffer tmp = new AutoBuffer();
        for (byte b : data) {
            if (b == 0x7D) {
                tmp.writeByte((byte) 0x7D);
                tmp.writeByte((byte) 0x01);
            } else if (b == 0x7E) {
                tmp.writeByte((byte) 0x7D);
                tmp.writeByte((byte) 0x02);
            } else {
                tmp.writeByte(b);
            }
        }
        return tmp.array();
    }

    public int getMessageSerialNo() {
        return header.getMessageSerialNo();
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }
}
